package com.joysuccess.bacnet.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.joysuccess.bacnet.service.BacnetClientService;
import com.joysuccess.bacnet.sources.mysql.mapper.CollectionPointMapper;
import com.joysuccess.bacnet.sources.mysql.model.SmCollectionPoint;
import com.joysuccess.common.utils.HelpUtils;
import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.RemoteDevice;
import com.serotonin.bacnet4j.type.Encodable;
import com.serotonin.bacnet4j.type.constructed.ObjectPropertyReference;
import com.serotonin.bacnet4j.type.constructed.SequenceOf;
import com.serotonin.bacnet4j.type.enumerated.ObjectType;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier;
import com.serotonin.bacnet4j.util.PropertyReferences;
import com.serotonin.bacnet4j.util.PropertyValues;
import com.serotonin.bacnet4j.util.RequestListener;
import com.serotonin.bacnet4j.util.RequestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author zhangqing
 */
@Service
public class BacnetClientServiceImpl implements BacnetClientService {
    private final static Logger LOGGER = LoggerFactory.getLogger(BacnetClientServiceImpl.class);


    @Autowired
    private LocalDevice localDevice;

    @Autowired
    private CollectionPointMapper collectionPointMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    private final static String BACNET_KAFKA_TOPIC="lengyuan-bacnet-datas";

//    /**
//     * 初始化:  冷源群控数据------->>>> MySQL
//     * @throws Exception
//     */
//    @Override
//    public void initBacnetDataToMySQL() throws Exception {
//        Iterator i$ = localDevice.getRemoteDevices().iterator();
//        while(i$.hasNext()) {
//            RemoteDevice d = (RemoteDevice)i$.next();
//            RequestUtils.getExtendedDeviceInformation(localDevice, d);
//            List oids = ((SequenceOf)RequestUtils.sendReadPropertyAllowNull(localDevice, d, d.getObjectIdentifier(), PropertyIdentifier.objectList)).getValues();
//            PropertyReferences refs = new PropertyReferences();
//            Iterator pvs = oids.iterator();
//
//            while(pvs.hasNext()) {
//                ObjectIdentifier oid = (ObjectIdentifier)pvs.next();
//                addPropertyReferences(refs, oid);
//            }
//
//            PropertyValues pvs1 = RequestUtils.readProperties(localDevice, d, refs, (RequestListener)null);
//
//            Iterator pvs1Itrator = pvs1.iterator();
//            while(pvs1Itrator.hasNext()) {
//
//                ObjectPropertyReference objectPropertyReference = (ObjectPropertyReference) pvs1Itrator.next();
//                Encodable encodable = pvs1.get(objectPropertyReference);
//                //根据ObjectType + instanceNumber 查询数据库，父表
//                String objetType = objectPropertyReference.getObjectIdentifier().getObjectType().toString();
//                int instanceNumber = objectPropertyReference.getObjectIdentifier().getInstanceNumber();
//
//                ObjectIdentifyModel objectIdentifyModel = new ObjectIdentifyModel(objetType,instanceNumber,1,d.getAddress().getMacAddress().getInetSocketAddress().getHostName());
//
//                Map<String,Object> paraMap = new HashMap<>();
//                paraMap.put("object_instance_number",instanceNumber);
//                paraMap.put("bacnet_ip",d.getAddress().getMacAddress().getInetSocketAddress().getHostName());
//                List<ObjectIdentifyModel> resultList = objectIdentifyMapper.selectByMap(paraMap);
//
//
//                //属性名称
//                PropertyIdentifier propertyIdentifier = objectPropertyReference.getPropertyIdentifier();
//                String property = propertyIdentifier.toString();
//                //属性值
//                String value = encodable.toString();
//                //Object 主键
//                int objectId;
//                /**
//                 * 未查询到增加
//                 */
//                if(property.equalsIgnoreCase("Object name")){
//                    objectIdentifyModel.setObjectName(value);
//                }
//                if(property.equalsIgnoreCase("description")){
//                    objectIdentifyModel.setDescription(value);
//                }
//                if(HelpUtils.isEmpty(resultList)){
//                    //没有查询到数据
//                    objectId = objectIdentifyMapper.insert(objectIdentifyModel);
//                }else{
//                    ObjectIdentifyModel objectIdentifyModel1 = resultList.get(0);
//                    objectId = objectIdentifyModel1.getId();
//                    objectIdentifyModel.setId(objectId);
//                    objectIdentifyMapper.updateById(objectIdentifyModel);
//                }
//            }
//        }
//    }

//    /**
//     * 初始化数据： 冷源群控在MySQL的数据 ------------->>> Redis
//     */
//    @Override
//    public boolean initRedisFromMySQLOnceAll() {
//        //查询数据库的数据
//        List<ObjectIdentifyModel> list = objectIdentifyMapper.selectList(null);
//        if(HelpUtils.isNotEmpty(list)){
//            for (ObjectIdentifyModel objectIdentifyModel : list) {
//                Integer number = objectIdentifyModel.getObjectInstanceNumber();
//                String ip = objectIdentifyModel.getBacnetIp();
//                String redisKey = "lengyuan:" + ip;
//                //按照类型拆分key
//                redisTemplate.opsForHash().put(redisKey,number.toString(),"");
//            }
//            return true;
//        }else {
//            return false;
//        }
//    }

    /**
     * 采集数据:  bacnet数据------>>>> Kafka中
     * @throws Exception
     */
    @Override
    public void initBacnetDataToKafka() throws Exception {
        Iterator remoteDeviceIterator = localDevice.getRemoteDevices().iterator();

        while(remoteDeviceIterator.hasNext()) {
            RemoteDevice d = (RemoteDevice)remoteDeviceIterator.next();
            RequestUtils.getExtendedDeviceInformation(localDevice, d);
            List oids = ((SequenceOf)RequestUtils.sendReadPropertyAllowNull(localDevice, d, d.getObjectIdentifier(), PropertyIdentifier.objectList)).getValues();
            PropertyReferences refs = new PropertyReferences();
            Iterator pvs = oids.iterator();

            while(pvs.hasNext()) {
                ObjectIdentifier oid = (ObjectIdentifier)pvs.next();
                addPresentValue(refs, oid);
            }

            PropertyValues pvs1 = RequestUtils.readProperties(localDevice, d, refs, (RequestListener)null);
            Iterator pvs1Itrator = pvs1.iterator();

            Map<Integer,Map<String,String>> valueMap = new HashMap<Integer,Map<String,String>>();

            while(pvs1Itrator.hasNext()) {
                ObjectPropertyReference objectPropertyReference = (ObjectPropertyReference) pvs1Itrator.next();
                Encodable encodable = pvs1.get(objectPropertyReference);
                int instanceNumber = objectPropertyReference.getObjectIdentifier().getInstanceNumber();
                String value = encodable.toString();
                PropertyIdentifier propertyIdentifier = objectPropertyReference.getPropertyIdentifier();
                String property = propertyIdentifier.toString();

                Map<String,String> res  = valueMap.get(instanceNumber);

                if(null != res){
                    String colKey = null;
                    String colValue = null;
                    if(property.equalsIgnoreCase("Object name")){
                        //说明之前肯定是加进去了PresentValue的值了，现在需要替换掉他的值
                        String presentValue = res.get(String.valueOf(instanceNumber));
                        //移除instance number
                        res.remove(String.valueOf(instanceNumber));
                        //增加Object Name作为唯一的值
                        res.put(value,presentValue);
                        colKey = value;
                        colValue = presentValue;
                    }
                    if(property.equalsIgnoreCase("Present Value")){
                        Set<String> keySets = res.keySet();
                        String typeNameCode = (String)keySets.toArray()[0];
                        res.put(typeNameCode,value);
                        colKey = typeNameCode;
                        colValue = value;
                    }

                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("objectName",colKey);
                    jsonObject.put("presentValue",colValue);
                    //还需要一个AssetId
                    Map<String,Object> map  = new HashMap<>();
                    map.put("point_code",colKey);
                    List<SmCollectionPoint> rlist = collectionPointMapper.selectByMap(map);
                    if(HelpUtils.isNotEmpty(rlist)){
                        //没有从数据库中查询到的数据，不进入kafka
                        SmCollectionPoint smCollectionPoint = rlist.get(0);
                        if(HelpUtils.isNotEmpty(smCollectionPoint.getAssertId())){
                            jsonObject.put("assetId",smCollectionPoint.getAssertId());
                            LOGGER.info("==========准备发送kafka，数据是：" + jsonObject.toJSONString());
                            kafkaTemplate.send(BACNET_KAFKA_TOPIC,jsonObject.toJSONString());
                        }
                    }
                }else{
                    if(property.equalsIgnoreCase("Object name")){
                        Map<String,String> objectNameMap =  new HashMap<String,String>();
                        objectNameMap.put(value,"");
                        valueMap.put(instanceNumber,objectNameMap);
                    }
                    if(property.equalsIgnoreCase("Present Value")){
                        Map<String,String> objectNameMap =  new HashMap<String,String>();
                        objectNameMap.put(String.valueOf(instanceNumber),value);
                        valueMap.put(instanceNumber,objectNameMap);
                    }
                }
            }
            //垃圾回收器回收
            valueMap = null;
        }
    }

    private static void addPresentValue(PropertyReferences refs, ObjectIdentifier oid){
        refs.add(oid, PropertyIdentifier.objectName);
        ObjectType type = oid.getObjectType();
        if(!ObjectType.analogInput.equals(type) && !ObjectType.analogOutput.equals(type) && !ObjectType.analogValue.equals(type) && !ObjectType.pulseConverter.equals(type)) {
            if(!ObjectType.binaryInput.equals(type) && !ObjectType.binaryOutput.equals(type) && !ObjectType.binaryValue.equals(type)) {
                if(!ObjectType.multiStateInput.equals(type) && !ObjectType.multiStateOutput.equals(type) && !ObjectType.multiStateValue.equals(type)) {
                    return;
                }
            }
        }
        refs.add(oid, PropertyIdentifier.presentValue);
    }

    private static void addPropertyReferences(PropertyReferences refs, ObjectIdentifier oid) {
        refs.add(oid, PropertyIdentifier.objectName);
        refs.add(oid, PropertyIdentifier.description);
        ObjectType type = oid.getObjectType();
        if(ObjectType.accumulator.equals(type)) {
//            refs.add(oid, PropertyIdentifier.units);
        } else if(!ObjectType.analogInput.equals(type) && !ObjectType.analogOutput.equals(type) && !ObjectType.analogValue.equals(type) && !ObjectType.pulseConverter.equals(type)) {
            if(!ObjectType.binaryInput.equals(type) && !ObjectType.binaryOutput.equals(type) && !ObjectType.binaryValue.equals(type)) {
                if(ObjectType.lifeSafetyPoint.equals(type)) {
//                    refs.add(oid, PropertyIdentifier.units);
                } else if(ObjectType.loop.equals(type)) {
//                    refs.add(oid, PropertyIdentifier.outputUnits);
                } else {
                    if(!ObjectType.multiStateInput.equals(type) && !ObjectType.multiStateOutput.equals(type) && !ObjectType.multiStateValue.equals(type)) {
                        return;
                    }

//                    refs.add(oid, PropertyIdentifier.stateText);
                }
            } else {
//                refs.add(oid, PropertyIdentifier.inactiveText);
//                refs.add(oid, PropertyIdentifier.activeText);
            }
        } else {
//            refs.add(oid, PropertyIdentifier.units);
        }
    }
}
